/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.biz.service.impl;

import HslCommunication.Core.Types.OperateResult;
import HslCommunication.Profinet.Siemens.SiemensPLCS;
import HslCommunication.Profinet.Siemens.SiemensS7Net;
import cn.hutool.core.convert.Convert;
import com.alipay.api.domain.JsonOpenApiVO;
import io.swagger.util.Json;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.biz.domain.*;
import me.zhengjie.modules.biz.repository.*;
import me.zhengjie.utils.ValidationUtil;
import me.zhengjie.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.biz.service.SensorService;
import me.zhengjie.modules.biz.service.dto.SensorDto;
import me.zhengjie.modules.biz.service.dto.SensorQueryCriteria;
import me.zhengjie.modules.biz.service.mapstruct.SensorMapper;
import org.checkerframework.checker.units.qual.A;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;

import java.lang.System;
import java.util.*;
import java.io.IOException;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;

import me.zhengjie.modules.biz.utils.SerialPortUtilTest;

/**
* @website https://eladmin.vip
* @description 服务实现
* @author Admin
* @date 2023-02-17
**/
@Service
@RequiredArgsConstructor
public class SensorServiceImpl implements SensorService {

    private final SensorRepository sensorRepository;
    private final SensorLogRepository sensorLogRepository;
    private final LabRepository labRepository;
    private final SensorMapper sensorMapper;
    private final MessageRepository messageRepository;
    private final ErrorLogRepository errorLogRepository;

    @Override
    public Map<String,Object> queryAll(SensorQueryCriteria criteria, Pageable pageable){
        Page<Sensor> page = sensorRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        Page<SensorDto> dto = page.map(sensorMapper::toDto);
        List<SensorDto> resultDto = dto.getContent();
        for (SensorDto sensorDto:resultDto) {
            Lab lab = labRepository.findById(sensorDto.getLabId()).orElseGet(Lab::new);
            sensorDto.setLabName(lab.getName());
        }

        return PageUtil.toPage(dto);
    }

    @Override
    public List<SensorDto> queryAll(SensorQueryCriteria criteria){
        Specification<Sensor> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            if (criteria.getName() != null) {
                predicates.add(criteriaBuilder.like(root.get("name"), "%" + criteria.getName() + "%"));
            }
            if (criteria.getDataType() != null) {
                predicates.add(criteriaBuilder.equal(root.get("dataType"), criteria.getDataType()));
            }
            //            if (criteria.getType() == null){
//                predicates.add(criteriaBuilder.or((criteriaBuilder.equal(root.get("type").as(Integer.class), 1)), (criteriaBuilder.equal(root.get("type").as(Integer.class), 2)), (criteriaBuilder.equal(root.get("type").as(Integer.class), 3))));
//            }else {
//                predicates.add(criteriaBuilder.equal(root.get("type"), criteria.getType()));
//            }
            if (criteria.getType() != null){
                predicates.add(criteriaBuilder.equal(root.get("type"), criteria.getType()));
            }
            if (criteria.getLabId()!=null){
                predicates.add(criteriaBuilder.equal(root.get("labId"), criteria.getLabId()));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        List<Sensor> sensorList = sensorRepository.findAll(specification);
        List<SensorDto> resultDto = sensorMapper.toDto(sensorList);
        for (SensorDto sensorDto:resultDto) {
            Lab lab = labRepository.findById(sensorDto.getLabId()).orElseGet(Lab::new);
            sensorDto.setLabName(lab.getName());
        }
        return resultDto;
    }

    @Override
    @Transactional
    public SensorDto findById(Integer id) {
        Sensor sensor = sensorRepository.findById(id).orElseGet(Sensor::new);
        ValidationUtil.isNull(sensor.getId(),"Sensor","id",id);
        return sensorMapper.toDto(sensor);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SensorDto create(Sensor resources) {
        int type = resources.getType();
        if (type !=2){
            resources.setDataType(2);
        }else {
            resources.setDataType(1);
        }
        return sensorMapper.toDto(sensorRepository.save(resources)
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Sensor resources) {
        Sensor sensor = sensorRepository.findById(resources.getId()).orElseGet(Sensor::new);
        ValidationUtil.isNull( sensor.getId(),"Sensor","id",resources.getId());
        sensor.copy(resources);
        sensorRepository.save(sensor);
    }

    @Override
    public void deleteAll(String[] createdAt) {
        sensorRepository.deleteAll(createdAt[0], createdAt[1]);
    }

    @Override
    public void download(List<SensorDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (SensorDto sensor : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("名称", sensor.getName());
            if (sensor.getType() == 5) {

            } else {
                String type = "";
                switch (sensor.getType()) {
                    case 1:
                        type = "温度";
                        break;
                    case 2:
                        type = "湿度";
                        break;
                    case 3:
                        type = "压差";
                        break;
                }
                map.put("传感器类型", type);
            }

            map.put("实验室", sensor.getLabName());
            map.put("阈值", sensor.getThresholdValue());
            map.put("传感器实时数据",  sensor.getData());
            map.put("ip", sensor.getIp());
            map.put("地址", sensor.getAddress());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
    @Override
    public void realTimeUpdate() {
        SerialPortUtilTest sensorData = new SerialPortUtilTest();
        sensorData.serialPortAction(1, data -> {
            if (!data.isEmpty()){
                int id = sensorRepository.findByThresholdValueAndData_type(Integer.parseInt(data.get(0)),1);
                setCurrentData(data, id);
            }
        });
        sensorData.serialPortAction(2, data -> {
            if (!data.isEmpty()){
                int id = sensorRepository.findByThresholdValueAndData_type(Integer.parseInt(data.get(0)),2);
                setCurrentData(data, id);
            }
        });
    }


    private void setCurrentData(ArrayList<String> data, int id) {
        Sensor sensor = sensorRepository.getSensor(id);
        sensor.setData((float) (Integer.parseInt(data.get(1))/1000));
        sensorRepository.save(sensor);
        sensor = sensorRepository.getSensor(id);
        int type = isError(Integer.parseInt(data.get(0)),(float) (Integer.parseInt(data.get(1))/1000));
//        int type = 0;
        setData(type,sensor);
    }

    private void setData(int type,Sensor sensor) {
        int id = sensor.getId();
        SensorLog log = new SensorLog();
        log.setSensorId(id);
        log.setData(sensor.getData());
        log.setType(type);
        log.setName(sensor.getName());
        sensorLogRepository.save(log);
    }

    private int isError(int slaveStation,float currentData){
        float thresholdValue = sensorRepository.findByThresholdValue(slaveStation);
        if(thresholdValue>=currentData){
            return 0;
        }else {
           // PhoneMessage.getPhonemsg("17895010215");
            return 1;
        }
    }

    @Override
    public ArrayList<Integer> queryAllByTimeAndId() {
        ArrayList<Integer> sensorId = sensorRepository.findAllByIdAndType();
        return sensorId;
    }

    @Override
    public Map<String, Object> querySensorByType(SensorQueryCriteria criteria, Pageable pageable) {
        Specification<Sensor> specification = (Specification<Sensor>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            if (criteria.getName() != null) {
                predicates.add(criteriaBuilder.like(root.get("name"), "%" + criteria.getName() + "%"));
            }
            if (criteria.getDataType() != null) {
                predicates.add(criteriaBuilder.equal(root.get("dataType"), criteria.getDataType()));
            }
//            if (criteria.getType() == null){
//                predicates.add(criteriaBuilder.or((criteriaBuilder.equal(root.get("type").as(Integer.class), 1)), (criteriaBuilder.equal(root.get("type").as(Integer.class), 2)), (criteriaBuilder.equal(root.get("type").as(Integer.class), 3))));
//            }else {
//                predicates.add(criteriaBuilder.equal(root.get("type"), criteria.getType()));
//            }
            if (criteria.getType() != null){
                predicates.add(criteriaBuilder.equal(root.get("type"), criteria.getType()));
            }
            if (criteria.getLabId()!=null){
                predicates.add(criteriaBuilder.equal(root.get("labId"), criteria.getLabId()));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<Sensor> page = sensorRepository.findAll(specification, pageable);
        Page<SensorDto> dto = page.map(sensorMapper::toDto);
        List<SensorDto> resultDto = dto.getContent();
        for (SensorDto sensorDto:resultDto) {
            Lab lab = labRepository.findById(sensorDto.getLabId()).orElseGet(Lab::new);
            sensorDto.setLabName(lab.getName());
        }

        return PageUtil.toPage(dto);

    }

    @Override
    public void setAlarmThreshold(String ip, String address, Float alarmThreshold) {
        SiemensS7Net siemensTcpNet = new SiemensS7Net(SiemensPLCS.S200, ip);
        OperateResult connect = siemensTcpNet.ConnectServer();
        if (connect.IsSuccess)
        {
            siemensTcpNet.Write(address, alarmThreshold);
        }
        siemensTcpNet.ConnectClose();
    }

    @Override
    public ArrayList<Map<String,Object>> listFloor(Integer floor) {
        ArrayList<Map<String,Object>> arrayList = new ArrayList<>();
        List<Sensor> sensorList = sensorRepository.findByFloor(floor);
        for (Sensor sensorDto : sensorList) {
            Map<String,Object> hashMap = new HashMap<>();
            hashMap.put("name",sensorDto.getName());
            hashMap.put("isError", sensorDto.getIsError());
            hashMap.put("data",sensorDto.getData());
            if (Objects.nonNull(sensorDto.getCoordinate())) {
                hashMap.put("position",sensorDto.getCoordinate().replace("[","").replace("]","").split(","));
            }
            arrayList.add(hashMap);
        }
        return arrayList;
    }

    @Override
    public List<Map<String, Object>> getCountByFloor() {
        return sensorRepository.getCountByFloor();
    }

    @Scheduled(cron = "0 0 * * * ?")
    public void queryPLCData() {

        List<Sensor> sensorList = sensorRepository.findAll();
        if (sensorList.size() < 1) {
            return;
        }

        String ip = null;
        String address = null;
        float data = 0;
        for (Sensor sensor : sensorList) {
            if (sensor.getIp() == null || sensor.getAddress() == null) {
                continue;
            }
            ip = sensor.getIp();
            address = sensor.getAddress();
            SiemensS7Net siemensTcpNet = new SiemensS7Net(SiemensPLCS.S200, ip);
            OperateResult connect = siemensTcpNet.ConnectServer( );
            if (!connect.IsSuccess)
            {
                siemensTcpNet.ConnectClose();
                continue;
            }

            try {
                data = siemensTcpNet.ReadFloat(address).Content;
                sensor.setData(data);
                sensorRepository.save(sensor);

            } catch (Exception ignored) {

            }

            siemensTcpNet.ConnectClose();
        }
    }

    @Scheduled(cron = "0 0 */6 * * ?")
    public void queryPLCAlarmDataLog() {

        List<Sensor> sensorList = sensorRepository.findAll();
        if (sensorList.size() < 1) {
            return;
        }

        String ip = null;
        String address = null;
        float data = 0;
        for (Sensor sensor : sensorList) {
            SensorLog sensorLog = new SensorLog();
            if (sensor.getIp() == null || sensor.getAddress() == null) {
                continue;
            }
            ip = sensor.getIp();
            address = sensor.getAddress();
            SiemensS7Net siemensTcpNet = new SiemensS7Net(SiemensPLCS.S200, ip);
            OperateResult connect = siemensTcpNet.ConnectServer( );
            if (!connect.IsSuccess)
            {
                siemensTcpNet.ConnectClose();
                continue;
            }

            try {
                data = siemensTcpNet.ReadFloat(address).Content;

                sensorLog.setType(sensor.getType());
                sensorLog.setSensorId(sensor.getId());
                sensorLog.setData(data);
                sensorLog.setLogType(0);
                sensorLogRepository.save(sensorLog);

            } catch (Exception ignored) {

            }

            siemensTcpNet.ConnectClose();
        }
    }

    @Scheduled(cron = "0 0 * * * ?")
    public void queryPLCAlarmData() {

        List<Sensor> sensorList = sensorRepository.findAll();
        if (sensorList.size() < 1) {
            return;
        }

        String ip = null;
        String address = null;
        float data = 0;
        for (Sensor sensor : sensorList) {
            SensorLog sensorLog = new SensorLog();
            if (sensor.getIp() == null || sensor.getAddress() == null) {
                continue;
            }
            ip = sensor.getIp();
            address = sensor.getAddress();
            SiemensS7Net siemensTcpNet = new SiemensS7Net(SiemensPLCS.S200, ip);
            OperateResult connect = siemensTcpNet.ConnectServer( );
            if (!connect.IsSuccess)
            {
                siemensTcpNet.ConnectClose();
                continue;
            }

            try {
                data = siemensTcpNet.ReadFloat(address).Content;

                sensorLog.setType(sensor.getType());
                sensorLog.setSensorId(sensor.getId());
                sensorLog.setData(data);

                Lab lab = labRepository.findById(sensor.getLabId()).orElseGet(Lab::new);

                if (data > sensor.getHighThresholdValue()) {
                    sensorLog.setLogType(1);
                    sensorLogRepository.save(sensorLog);

                    ErrorLog errorLog = new ErrorLog();
                    errorLog.setName(sensor.getName());
                    errorLog.setFloor(lab.getFloor());
                    errorLog.setPositionId(lab.getId());
                    errorLog.setPosition(lab.getName());
                    errorLog.setStatus(0);
                    errorLog.setDec("超出高位阈值");
                    errorLog.setValue(Convert.toFloat(data));
                    errorLogRepository.save(errorLog);
                } else if (data < sensor.getLowThresholdValue()) {
                    sensorLog.setLogType(1);
                    sensorLogRepository.save(sensorLog);

                    ErrorLog errorLog = new ErrorLog();
                    errorLog.setName(sensor.getName());
                    errorLog.setFloor(lab.getFloor());
                    errorLog.setPositionId(lab.getId());
                    errorLog.setPosition(lab.getName());
                    errorLog.setStatus(0);
                    errorLog.setDec("超出低位阈值");
                    errorLog.setValue(Convert.toFloat(data));
                    errorLogRepository.save(errorLog);
                } else {
                    sensorLog.setLogType(0);
                    sensorLogRepository.save(sensorLog);
                }

            } catch (Exception ignored) {

            }

            siemensTcpNet.ConnectClose();
        }
    }

//    @Scheduled(cron = "* */5 * * *  ?")
//    public void queryPLCAlarmData() {
//
//        List<Sensor> sensorList = sensorRepository.findAll();
//        if (sensorList.size() < 1) {
//            return;
//        }
//        String ip = null;
//        String address = null;
//        byte byteData;
//        int intData;
//        float data = 0;
//        for (Sensor sensor : sensorList) {
//            SensorLog sensorLog = new SensorLog();
//            if (sensor.getIp() == null || Objects.isNull(sensor.getAlarmAddress())) {
//                continue;
//            }
//            ip = sensor.getIp();
//            address = sensor.getAlarmAddress();
//            SiemensS7Net siemensTcpNet = new SiemensS7Net(SiemensPLCS.S200, ip);
//            OperateResult connect = siemensTcpNet.ConnectServer( );
//            if (!connect.IsSuccess)
//            {
//                siemensTcpNet.ConnectClose();
//                continue;
//            }
//
//            try {
//                byteData = siemensTcpNet.ReadByte(address).Content;
//                intData = byteData & 1;
//                data = siemensTcpNet.ReadFloat(address).Content;
//                sensor.setData(data);
//
//                if (Objects.equals(intData, 1)) {
//                    sensorLog.setType(sensor.getType());
//                    sensorLog.setSensorId(sensor.getId());
//                    sensorLog.setData(data);
//                    sensorLog.setLogType(1);
//                    sensorLogRepository.save(sensorLog);
//
//                    Message message = new Message();
//                    message.setName(sensor.getName() + "气体浓度超过阀值");
//                    message.setType(1);
//                    Lab lab = labRepository.findById(sensor.getLabId()).orElseGet(Lab::new);
//                    ValidationUtil.isNull(lab.getId(),"Lab","id",sensor.getId());
//                    message.setUserId(lab.getAdministrator());
//                    messageRepository.save(message);
//
//                    ErrorLog errorLog = new ErrorLog();
//                    errorLog.setName(sensor.getName());
//                    errorLog.setPosition(lab.getName());
//                    errorLog.setType(3);
//                    errorLog.setValue(3);
//                    errorLog.setFloor(lab.getFloor());
//
//                    if (Objects.equals(sensor.getType(), 4)) {
//                        errorLog.setErrorCode("氧传感器报警");
//                        errorLog.setDec("氧传感器报警");
//                    }
//                    if (Objects.equals(sensor.getType(), 6)) {
//                        errorLog.setErrorCode("乙炔传感器报警");
//                        errorLog.setDec("乙炔传感器报警");
//                    }
//                    if (Objects.equals(sensor.getType(), 7)) {
//                        errorLog.setErrorCode("氢传感器报警");
//                        errorLog.setDec("氢传感器报警");
//                    }
//                    errorLogRepository.save(errorLog);
//                }
//
//            } catch (Exception ignored) {
//                throw new BadRequestException("");
//            }
//
//            siemensTcpNet.ConnectClose();
//        }
//    }


}
